參考 Bomb Lab 實作紀錄,並在 x86 的電腦上進行簡易的實作
這題目設計的重點就是這兩個 phase ,要想辦法在 gdb 中繞過這兩個 condition,避免他觸發 exit(1)
void phase_1() {
int x=12;
if(x==12) {
system("google-chrome --incognito pornhub.com");
exit(1);
} else {
printf("pass phase_1\n");
}
}
void phase_2() {
char text[] = "pornhub";
char input[100];
scanf("%s", input);
if(strcmp(input, text)==0) {// match
printf("pass phase_2\n");
} else {
exit(1);
}
}
開始執行 gdb 後,記得把斷點設在 phase_1 / phase_2,至於怎麼執行,到底是要用 step 還是 stepi,請自行判斷,以下只會節錄重點
(gdb) r
Starting program: /home/demonic/gitPro/ithome2020/day3Bomb/bomb
Missing separate debuginfos, use: zypper install glibc-debuginfo-2.26-lp152.25.10.x86_64
[Detaching after fork from child process 18108]
sh: google-chrome: command not found
[Inferior 1 (process 18104) exited with code 01]
(gdb) b phase_2
Breakpoint 1 at 0x400695: file bomb.c, line 16.
(gdb) b phase_1
Breakpoint 2 at 0x40065f: file bomb.c, line 6.
以下組語語法是 AT&T 系列的
Dump of assembler code for function phase_1:
0x0000000000400657 <+0>: push %rbp
0x0000000000400658 <+1>: mov %rsp,%rbp
0x000000000040065b <+4>: sub $0x10,%rsp
=> 0x000000000040065f <+8>: movl $0xc,-0x4(%rbp)
0x0000000000400666 <+15>: cmpl $0xc,-0x4(%rbp)
0x000000000040066a <+19>: jne 0x400680 <phase_1+41>
0x000000000040066c <+21>: mov $0x400798,%edi
0x0000000000400671 <+26>: callq 0x400540 <system@plt>
0x0000000000400676 <+31>: mov $0x1,%edi
0x000000000040067b <+36>: callq 0x400570 <exit@plt>
0x0000000000400680 <+41>: mov $0x4007be,%edi
0x0000000000400685 <+46>: callq 0x400530 <puts@plt>
0x000000000040068a <+51>: nop
0x000000000040068b <+52>: leaveq
0x000000000040068c <+53>: retq
可以發現,重點是要去找 rbp 這個 register,然後對他 -0x4 的位置,進行操作,把數值從 0xc 改掉,改成任意其他數字都好
(gdb) p $rbp
$14 = (void *) 0x7fffffffe560
(gdb) p 0x7fffffffe560
$15 = 140737488348512
(gdb) p *0x7fffffffe560
$16 = -6800
(gdb) p *(0x7fffffffe560-0x4)
$17 = 12
(gdb) p *(0x7fffffffe560-0x4)=2
$18 = 2
(gdb) p *(0x7fffffffe560-0x4)
$19 = 2
Dump of assembler code for function phase_2:
0x000000000040068d <+0>: push %rbp
0x000000000040068e <+1>: mov %rsp,%rbp
0x0000000000400691 <+4>: sub $0x70,%rsp
=> 0x0000000000400695 <+8>: movabs $0x6275686e726f70,%rax
0x000000000040069f <+18>: mov %rax,-0x8(%rbp)
0x00000000004006a3 <+22>: lea -0x70(%rbp),%rax
0x00000000004006a7 <+26>: mov %rax,%rsi
0x00000000004006aa <+29>: mov $0x4007cb,%edi
0x00000000004006af <+34>: mov $0x0,%eax
0x00000000004006b4 <+39>: callq 0x400560 <__isoc99_scanf@plt>
0x00000000004006b9 <+44>: lea -0x8(%rbp),%rdx
0x00000000004006bd <+48>: lea -0x70(%rbp),%rax
0x00000000004006c1 <+52>: mov %rdx,%rsi
0x00000000004006c4 <+55>: mov %rax,%rdi
0x00000000004006c7 <+58>: callq 0x400550 <strcmp@plt>
0x00000000004006cc <+63>: test %eax,%eax
0x00000000004006ce <+65>: jne 0x4006dc <phase_2+79>
0x00000000004006d0 <+67>: mov $0x4007ce,%edi
0x00000000004006d5 <+72>: callq 0x400530 <puts@plt>
0x00000000004006da <+77>: jmp 0x4006e6 <phase_2+89>
0x00000000004006dc <+79>: mov $0x1,%edi
0x00000000004006e1 <+84>: callq 0x400570 <exit@plt>
0x00000000004006e6 <+89>: leaveq
0x00000000004006e7 <+90>: retq
這邊的重點是要先 step 到 scanf,然後隨便輸入一組字串,接著 step 到 strcmp
這種東西都有很多改法,我覺得從 test %eax, %eax 下手也是行得通,不過這邊我打算去讀 "pornhub" 的字串到底存在哪裡,把那個字串讀出來,讓我知道要輸入什麼即可~~
其實從組語中,很輕易可以觀察到,scanf 所使用的記憶體位置是 -0x70(%rbp),而 pornhub 字串肯定存在 -0x8(%rbp),我們只需要把他打印出來,即可知道 scanf 要輸入什麼~~
(gdb) p $rbp-0x8
$4 = (void *) 0x7fffffffe558
(gdb) x $rbp-0x8
0x7fffffffe558: 0x6e726f70
(gdb) p (char*)$rbp-0x8
$5 = 0x7fffffffe558 "pornhub"
從上面兩個簡單的範例,我們就可以感受到 gdb 的強大,可以任意修改數值,當然也可以修改程式碼,讓那一行直接變成另外一條指令,只是我懶得去查 opcode 在那邊修修改改
而且 GDB 可做到的可不只是這樣,甚至可以 reverse debugging,就是當你已經執行到下一步,仍舊可以返回,這部分可以參考 GDB and Reverse Debugging,但這種功能並不支援所有的平台,像我最近在開發的 RISC-V 就沒支援這東西,或許我吃飽太閒的時候,可以把他 porting 上去~~
下一篇會介紹 ptrace 要怎麼使用,並使用 ptrace 製作一個簡易的 breakpoint,這邊要注意一件事,ptrace 會使用到 parent/child process 的概念,可能會看得很問號
除此之外,接下來要寫的文章我要稍做一些調整,會加入 systemtap 以及 debuginfod 這兩個部分